﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web.Routing;
using Web7.ExceptionHandling;
using System.Web;


namespace Web7.Container.ExceptionPublishers
{
	public class WebResponseExceptionPublisher : IExceptionPublisher
	{
		public void Publish(Exception ex)
		{
			Publish(ex, null, null);
		}

		public void Publish(Exception exception, ExceptionContext context, IController controller)
		{
			OperationProcessLevel level = exception.GetLevel();

			if (level < OperationProcessLevel.Warning)
			{
				return;
			}

			var message = "异常消息未予以显示";
			var stackTrace = "异常的调用堆栈未予以显示";
			var formatNotice = "系统于{0}发生了异常：\r\n{1}\r\n\r\n错误堆栈跟踪信息：\r\n{2}";

#if DEBUG
			message = exception.Message;
			stackTrace = exception.StackTrace;
#endif

			var text = string.Format(formatNotice, DateTime.Now, message, stackTrace);

			bool processedRequest = false;
			if (null != context &&
				null != controller)
			{

				if (context.HttpContext.Request.IsAjaxRequest())
				{
					context.Result = new JsonResult {Data = text};
				}
				else
				{
					context.HttpContext.Response.Clear();
					context.HttpContext.Server.ClearError();
					var routeData = new RouteData();
					routeData.Values["controller"] = "Error";
					routeData.Values["action"] = "Index";

					routeData.Values["exception"] = exception;
					routeData.Values["message"] = text;
					routeData.Values["exceptionContext"] = context;
									 
					var rc = new RequestContext(new HttpContextWrapper(HttpContext.Current), routeData);
					try
					{
						controller.Execute(rc);
						processedRequest = true;
					}
					catch(Exception x)
					{
						x.SetLevel(OperationProcessLevel.Warning);
						x.PublishTo<LogExceptionPublisher>();
						// 指定的 ErrorController 执行失败，在下面的流程中会继续输出异常信息，无需处理该异常
					}
				}
			}
			
			if(!processedRequest){
				var httpContext = HttpContext.Current;
				var isAjaxRequest = httpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest";
				if (isAjaxRequest)
				{
					httpContext.Response.Write(String.Format("{{error:500, message:\"{0}\", data: null}}",
					                                         encodeJavascriptString(text)));
				}
				else
				{
					httpContext.Response.Clear();
					httpContext.Response.Write(text);
				}
			}

		}

		private string encodeJavascriptString(string str)
		{
			return str.Replace("\"", "\\\"");
		}

		public void Initialize(System.Collections.Specialized.NameValueCollection parameters)
		{
			
		}
	}
}

namespace Web7.Container
{
	using Web7;
	using Web7.ExceptionHandling;
	using Web7.Container.ExceptionPublishers;

	public class HandleExceptionAttribute : HandleErrorAttribute
	{

		public HandleExceptionAttribute(Type errorControllerType)
		{
			ErrorController = errorControllerType;
		}

		public Type ErrorController { get; set; }

		public override void OnException(ExceptionContext filterContext)
		{
			// base.OnException(filterContext);


			if (filterContext == null)
			{
				throw new ArgumentNullException("filterContext");
			}
			if (filterContext.IsChildAction)
			{
				return;
			}

			// If custom errors are disabled, we need to let the normal ASP.NET exception handler execute so that the user can see useful debugging information.
			if (!filterContext.HttpContext.IsCustomErrorEnabled)
			{
				//	return;
			}

			var exception = filterContext.Exception;

			// If this is not an HTTP 500 (for example, if somebody throws an HTTP 404 from an action method),
			// ignore it.
			if (new HttpException(null, exception).GetHttpCode() != 500)
			{
				return;
			}

			if (!ExceptionType.IsInstanceOfType(exception))
			{
				return;
			}

			if (null == ErrorController)
			{
				return;
			}

			exception.SetLevel(OperationProcessLevel.Warning);
			var publisher = ExceptionPublisherManager.AllPublishers.FirstOrDefault(x => x is WebResponseExceptionPublisher);

			WebResponseExceptionPublisher publisherItem = publisher as WebResponseExceptionPublisher;
			if (null != publisherItem)
			{
				publisherItem.Publish(exception, filterContext,
									  Activator.CreateInstance(ErrorController) as IController);
			}
			filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
			HttpContext.Current.Server.ClearError();
		}

	}

}
